#include "portmanager.h"
#include "configmanager.h"
#include "simplefocfindpacket.h"
#include "common/hplcqinfinefindpacket.h"
#include "hplcsparklinkfindpacket.h"
#include <QDebug>
#include <QTimer>

SINGLETON_IMPLIMENT(PortManager)

PortManager::PortManager(QObject *parent)
    : BaseManager (parent)
{
    qDebug() << "PortManager...";
    qRegisterMetaType<SerialPortSetting>("SerialPortSetting");
    m_mutex = new QMutex(QMutex::Recursive);
    m_ports.clear();

    m_serialPortConfig.clear();
    m_serialPortConfig =
    {
        {PortID_Main, {"Port Main", CK_Uart_AutoOpen, CK_Uart_Name, CK_Uart_BaudRate, CK_Uart_DataBits, CK_Uart_StopBits, CK_Uart_Parity}},
        {PortID_Debug, {"Port Debug", CK_DebugUart_AutoOpen, CK_DebugUart_Name, CK_DebugUart_BaudRate, CK_DebugUart_DataBits, CK_DebugUart_StopBits, CK_DebugUart_Parity}},
        {PortID_PumpA, {"Port PumpA", CK_PumpAUart_AutoOpen, CK_PumpAUart_Name, CK_PumpAUart_BaudRate, CK_PumpAUart_DataBits, CK_PumpAUart_StopBits, CK_PumpAUart_Parity}},
        {PortID_PumpB, {"Port PumpB", CK_PumpBUart_AutoOpen, CK_PumpBUart_Name, CK_PumpBUart_BaudRate, CK_PumpBUart_DataBits, CK_PumpBUart_StopBits, CK_PumpBUart_Parity}},
        {PortID_PumpC, {"Port PumpC", CK_PumpCUart_AutoOpen, CK_PumpCUart_Name, CK_PumpCUart_BaudRate, CK_PumpCUart_DataBits, CK_PumpCUart_StopBits, CK_PumpCUart_Parity}},
        {PortID_PumpD, {"Port PumpD", CK_PumpDUart_AutoOpen, CK_PumpDUart_Name, CK_PumpDUart_BaudRate, CK_PumpDUart_DataBits, CK_PumpDUart_StopBits, CK_PumpDUart_Parity}},
        {PortID_Detector, {"Port Detector", CK_DetectorUart_AutoOpen, CK_DetectorUart_Name, CK_DetectorUart_BaudRate, CK_DetectorUart_DataBits, CK_DetectorUart_StopBits, CK_DetectorUart_Parity}},
        {PortID_Collector, {"Port Collector", CK_CollectorUart_AutoOpen, CK_CollectorUart_Name, CK_CollectorUart_BaudRate, CK_CollectorUart_DataBits, CK_CollectorUart_StopBits, CK_CollectorUart_Parity}},
        {PortID_AutoSampler, {"Port AutoSampler", CK_AutoSamplerUart_AutoOpen, CK_AutoSamplerUart_Name, CK_AutoSamplerUart_BaudRate, CK_AutoSamplerUart_DataBits, CK_AutoSamplerUart_StopBits, CK_AutoSamplerUart_Parity}},
        {PortID_SimpleFoc, {"Port SimpleFoc", CK_SimpleFocUart_AutoOpen, CK_SimpleFocUart_Name, CK_SimpleFocUart_BaudRate, CK_SimpleFocUart_DataBits, CK_SimpleFocUart_StopBits, CK_SimpleFocUart_Parity}},
        {PortID_ElectronicScale_LiChen, {"Port ElectroniScale LiChen", CK_ElectronicScaleLiChenUart_AutoOpen, CK_ElectronicScaleLiChenUart_Name, CK_ElectronicScaleLiChenUart_BaudRate, CK_ElectronicScaleLiChenUart_DataBits, CK_ElectronicScaleLiChenUart_StopBits, CK_ElectronicScaleLiChenUart_Parity}},
        {PortID_ElectronicScale_Shimadzu, {"Port ElectroniScale Shimadzu", CK_ElectronicScaleShimadzuUart_AutoOpen, CK_ElectronicScaleShimadzuUart_Name, CK_ElectronicScaleShimadzuUart_BaudRate, CK_ElectronicScaleShimadzuUart_DataBits, CK_ElectronicScaleShimadzuUart_StopBits, CK_ElectronicScaleShimadzuUart_Parity}},
    };

    qDebug() << "PortManager...completed";
}

PortManager::~PortManager()
{
    foreach (auto p, m_ports)
    {
        if (p->isOpen())
            p->close();
    }
    delete  m_mutex;
}

bool PortManager::attach(BasePort *port)
{
    QMutexLocker locker(m_mutex);
    if (port == nullptr)
        return false;
    qDebug() << "attach port : " << port->getName();

    if (m_ports.contains(port->getMagic()))
    {
        qWarning() << "attach port(" << port->getName() << ") failed, because repeat magic";
        return false;
    }

    m_ports.insert(port->getMagic(), port);

    port->moveToThread(thread());

    return true;
}

bool PortManager::detach(BasePort *port)
{
    QMutexLocker locker(m_mutex);
    if (port == nullptr)
        return false;
    qDebug() << "detach port : " << port->getName();

    if (!m_ports.contains(port->getMagic()))
        return false;

    m_ports.remove(port->getMagic());
    return true;
}

void PortManager::print()
{
    QMutexLocker locker(m_mutex);
    qDebug() << "===========Print All Ports==============";
    int i = 0;
    foreach (auto p, m_ports)
    {
        qDebug() << i << ", " << p->getName();
        i++;
    }
    qDebug() << "===========End All Ports==============";
}

BasePort *PortManager::find(QString &name)
{
    QMutexLocker locker(m_mutex);
    foreach(auto p, m_ports)
    {
        if (p->getName() == name)
        {
            return p;
        }
    }
    return nullptr;
}

BasePort *PortManager::find(PortID id)
{
    QMutexLocker locker(m_mutex);
    foreach(auto p, m_ports)
    {
        if (p->getID() == id)
        {
            return p;
        }
    }
    return nullptr;
}

BasePort *PortManager::find(uint magic)
{
    QMutexLocker locker(m_mutex);
    if (m_ports.contains(magic))
        return m_ports.value(magic);
    return nullptr;
}

QList<BasePort *> PortManager::getAllPorts()
{
    QMutexLocker locker(m_mutex);
    return m_ports.values();
}

uint PortManager::createMagic(QString str)
{
    QMutexLocker locker(m_mutex);
    uint magic = qHash(str);
    if (m_ports.contains(magic))
    {
        magic = qHash(str + str);
        if (m_ports.contains(magic))
        {
            qWarning() << "Repeat Magic";
        }
    }
    return magic;
}

void PortManager::onStart(void)
{
    QMutexLocker locker(m_mutex);
    qDebug() << "PortManager onStart...";

    QList<uint> list = m_serialPortConfig.keys();

    foreach (auto id, list)
    {
        initSerialPort((PortID)id);
    }

    QTimer::singleShot(0, [=](){
        checkSerialPortAutoOpen();
    });

    emit started();

    qDebug() << "PortManager onStart...completed";
}

bool PortManager::saveSerialPort(PortID id, SerialPortSetting &setting)
{
    QMutexLocker locker(m_mutex);

    SerialPort *p = static_cast<SerialPort*>(find(id));
    if (p == nullptr)
    {
        qDebug() << "Cann't find SerialPort";
        return false;
    }
    SerialPortConfig config;
    if (m_serialPortConfig.contains(id))
    {
        config = m_serialPortConfig.value(id);
    }
    else
    {
        qDebug() << "Cann't find the config";
        return false;
    }

    QVariant var = p->getConfig();
    SerialPortSetting s = var.value<SerialPortSetting>();
//    if (setting.name != s.name)
    {
        configManager->set(config.name, setting.name);
        qDebug() << "name = " << setting.name;
    }
//    if (setting.baudRate != s.baudRate)
    {
        configManager->set(config.baudRate, QString::number(setting.baudRate));
    }
//    if (setting.dataBits != s.dataBits)
    {
        configManager->set(config.dataBits, SerialPort::fromDataBit(setting.dataBits));
    }
//    if (setting.stopBits != s.stopBits)
    {
        configManager->set(config.stopBits, SerialPort::fromStopBit(setting.stopBits));
    }
//    if (setting.parity != s.parity)
    {
        configManager->set(config.parity, SerialPort::fromParityBit(setting.parity));
    }
//    if (setting.isAutoOpen != s.isAutoOpen)
    {
        configManager->set(config.autoOpen, setting.isAutoOpen);
    }

    return true;
}

PortManager::SerialPortConfig PortManager::getSerialPortConfig(PortID id)
{
    if (m_serialPortConfig.contains(id))
    {
        return m_serialPortConfig.value(id);
    }
    else
    {
        qDebug() << "Cann't find the id in m_serialPortConfig";
        return SerialPortConfig();
    }
}

bool PortManager::initSerialPort(PortID id)
{
    QMutexLocker locker(m_mutex);

    // SerialPort init
    SerialPort *serialPort = new SerialPort(); // 由于要moveToThread，所以不能带parent
    if (serialPort == nullptr)
    {
        qDebug() << "Cann't new SerialPort";
        return false;
    }
    SerialPortConfig config;
    if (m_serialPortConfig.contains(id))
    {
        config = m_serialPortConfig.value(id);
    }
    else
    {
        qDebug() << "Cann't find the config";
        return false;
    }

    serialPort->setName(config.label);
    serialPort->setID(id);
    uint magic = createMagic(config.label);
    serialPort->setMagic(magic);

    SerialPortSetting setting;
    setting.name = configManager->get(config.name).toString();
    setting.baudRate = configManager->get(config.baudRate).toInt();
    setting.parity = SerialPort::toParityBit(configManager->get(config.parity).toString());
    setting.dataBits = SerialPort::toDataBit(configManager->get(config.dataBits).toString());
    setting.stopBits = SerialPort::toStopBit(configManager->get(config.stopBits).toString());
    setting.openMode = QSerialPort::ReadWrite;
    setting.isAutoOpen = configManager->get(config.autoOpen).toBool();
    QVariant var;
    var.setValue(setting);
    serialPort->setConfig(var);

    if (!attach(serialPort))
    {
        serialPort->deleteLater();
        return false;
    }
    return true;
}

void PortManager::checkSerialPortAutoOpen()
{
    QMutexLocker locker(m_mutex);

    QList<uint> list = m_serialPortConfig.keys();

    foreach (auto id, list)
    {
        SerialPortConfig config = m_serialPortConfig.value(id);

        bool isAutoOpen = configManager->get(config.autoOpen).toBool();
        if (isAutoOpen)
        {
            // 最后才打开串口
            BasePort *port = find(id);
            if (port == nullptr)
                return;

            port->start();
            if (!port->isOpen())
            {
                qDebug() << "SerialPort open error" << port->getName();
            }
            else
            {
                SerialPortSetting setting;
                QVariant var = port->getConfig();
                setting = var.value<SerialPortSetting>();

                qDebug() << "Open~~~~~~~~~!!!!!!" << port->getName()
                 << setting.name;
            }
        }
    }
}
